import subprocess
import os
import platform
import time
import re
import logging
from django.core.cache import cache
from django.utils import timezone
from utils.system_format import get_memory_usage_percentage, get_disk_usage_percentage

logger = logging.getLogger(__name__)

CACHE_KEY = "system:info"
CACHE_TTL = 300  # 5分钟


class SystemService:
    @staticmethod
    def get_current_system_info():
        cached = cache.get(CACHE_KEY)
        if cached:
            # 检查是否过期（额外判断）
            last_update = cached.get("last_update_time")
            if last_update and (timezone.now().timestamp() - last_update) < 300:
                logger.info("从缓存获取系统信息")
                return cached

        logger.info("缓存过期或不存在，重新采集系统信息")
        system_info = SystemService.collect_system_info()
        cache.set(CACHE_KEY, system_info, timeout=CACHE_TTL)
        return system_info

    @staticmethod
    def get_system_metrics():
        return SystemService.collect_system_info()

    @staticmethod
    def refresh_system_info():
        system_info = SystemService.collect_system_info()
        cache.set(CACHE_KEY, system_info, timeout=CACHE_TTL)
        logger.info("系统信息已刷新")
        return system_info

    @staticmethod
    def collect_system_info():
        system_info = {
            "hostname": "unknown",
            "os": platform.system().lower(),
            "arch": platform.machine(),
            "cpu_cores": os.cpu_count(),
            "cpu_usage": 0.0,
            "cpu_model": "Unknown",
            "memory_total": 0,
            "memory_used": 0,
            "memory_usage": 0.0,
            "disk_total": 0,
            "disk_used": 0,
            "disk_usage": 0.0,
            "network_in": 0,
            "network_out": 0,
            "uptime": 0,
            "load_avg_1": 0.0,
            "load_avg_5": 0.0,
            "load_avg_15": 0.0,
            "process_count": 0,
            "os_version": "Unknown",
            "last_update_time": timezone.now().timestamp()
        }

        try:
            system_info["hostname"] = platform.node()
        except Exception as e:
            logger.warning(f"获取主机名失败: {e}")

        os_type = platform.system().lower()
        if os_type in ['linux', 'darwin']:
            SystemService._collect_unix_info(system_info)
        elif os_type == 'windows':
            logger.info("Windows 系统暂不支持详细采集")
        else:
            logger.warning(f"不支持的操作系统: {os_type}")

        # 计算使用率
        system_info["memory_usage"] = get_memory_usage_percentage(system_info)
        system_info["disk_usage"] = get_disk_usage_percentage(system_info)

        return system_info

    @staticmethod
    def _collect_unix_info(info):
        try:
            info["cpu_usage"] = SystemService._get_cpu_usage()
            info["cpu_model"] = SystemService._get_cpu_model()
            mem_info = SystemService._get_memory_info()
            info.update({
                "memory_total": mem_info["total"],
                "memory_used": mem_info["used"]
            })
            disk_info = SystemService._get_disk_info()
            info.update({
                "disk_total": disk_info["total"],
                "disk_used": disk_info["used"]
            })
            load_avg = SystemService._get_load_average()
            info.update({
                "load_avg_1": load_avg[0],
                "load_avg_5": load_avg[1],
                "load_avg_15": load_avg[2]
            })
            info["uptime"] = SystemService._get_uptime()
            info["process_count"] = SystemService._get_process_count()
            info["os_version"] = SystemService._get_os_version()
            net_info = SystemService._get_network_traffic()
            info.update({
                "network_in": net_info["in"],
                "network_out": net_info["out"]
            })
        except Exception as e:
            logger.error(f"采集Unix系统信息失败: {e}")

    @staticmethod
    def _get_cpu_usage():
        try:
            if platform.system() == "Linux":
                with open('/proc/stat', 'r') as f:
                    line = f.readline()
                parts = line.strip().split()
                if parts[0] != 'cpu':
                    return 0.0
                times = list(map(int, parts[1:5]))
                idle = times[3]
                total = sum(times)
                # 采样两次
                time.sleep(1)
                with open('/proc/stat', 'r') as f:
                    line = f.readline()
                parts = line.strip().split()
                times2 = list(map(int, parts[1:5]))
                idle2 = times2[3]
                total2 = sum(times2)
                delta_total = total2 - total
                delta_idle = idle2 - idle
                if delta_total == 0:
                    return 0.0
                usage = 100.0 * (delta_total - delta_idle) / delta_total
                return round(usage, 2)
            elif platform.system() == "Darwin":
                output = subprocess.check_output(['top', '-l', '1', '-n', '0'], text=True)
                for line in output.splitlines():
                    if 'CPU usage' in line:
                        match = re.search(r'(\d+\.\d+)%', line)
                        if match:
                            return float(match.group(1))
        except Exception as e:
            logger.error(f"获取CPU使用率失败: {e}")
        return 0.0

    @staticmethod
    def _get_cpu_model():
        try:
            if platform.system() == "Linux":
                result = subprocess.check_output(['cat', '/proc/cpuinfo'], text=True)
                for line in result.splitlines():
                    if line.startswith('model name'):
                        return line.split(':', 1)[1].strip()
            elif platform.system() == "Darwin":
                return subprocess.check_output(['sysctl', '-n', 'machdep.cpu.brand_string'], text=True).strip()
        except Exception as e:
            logger.warning(f"获取CPU型号失败: {e}")
        return "Unknown"

    @staticmethod
    def _get_memory_info():
        try:
            if platform.system() == "Linux":
                with open('/proc/meminfo', 'r') as f:
                    lines = f.readlines()
                meminfo = {}
                for line in lines:
                    parts = line.strip().split(':')
                    if len(parts) == 2:
                        key = parts[0].strip()
                        value = parts[1].strip().split()[0]
                        try:
                            meminfo[key] = int(value) // 1024  # 转MB
                        except:
                            pass
                total = meminfo.get("MemTotal", 0)
                available = meminfo.get("MemAvailable", None)
                if available is not None:
                    used = total - available
                else:
                    free = meminfo.get("MemFree", 0)
                    used = total - free
                return {"total": total, "used": used}
            elif platform.system() == "Darwin":
                total_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize'], text=True))
                vm_stat = subprocess.check_output(['vm_stat'], text=True)
                lines = vm_stat.splitlines()
                free_pages = inactive_pages = 0
                for line in lines:
                    if "Pages free:" in line:
                        free_pages = int(re.findall(r'\d+', line)[0])
                    elif "Pages inactive:" in line:
                        inactive_pages = int(re.findall(r'\d+', line)[0])
                page_size = 4096
                free_bytes = (free_pages + inactive_pages) * page_size
                used_bytes = total_bytes - free_bytes
                return {
                    "total": total_bytes // (1024 ** 2),
                    "used": used_bytes // (1024 ** 2)
                }
        except Exception as e:
            logger.error(f"获取内存信息失败: {e}")
        return {"total": 0, "used": 0}

    @staticmethod
    def _get_disk_info():
        try:
            if platform.system() == "Darwin":
                output = subprocess.check_output(['df', '-k'], text=True)
                lines = output.strip().splitlines()
                max_usage = 0
                target_total = target_used = 0
                for line in lines[1:]:
                    fields = line.split()
                    if len(fields) >= 6:
                        mount = fields[-1]
                        if mount in ["/System/Volumes/Data", "/"]:
                            total_kb = int(fields[1])
                            used_kb = int(fields[2])
                            usage = used_kb / total_kb
                            if usage > max_usage:
                                max_usage = usage
                                target_total = total_kb // (1024 ** 2)  # GB
                                target_used = used_kb // (1024 ** 2)
                if target_total > 0:
                    return {"total": target_total, "used": target_used}
            # fallback
            output = subprocess.check_output(['df', '-k', '/'], text=True)
            lines = output.strip().splitlines()
            if len(lines) < 2:
                return {"total": 0, "used": 0}
            fields = lines[1].split()
            total_kb = int(fields[1])
            used_kb = int(fields[2])
            return {
                "total": total_kb // (1024 ** 2),
                "used": used_kb // (1024 ** 2)
            }
        except Exception as e:
            logger.error(f"获取磁盘信息失败: {e}")
        return {"total": 0, "used": 0}

    @staticmethod
    def _get_load_average():
        try:
            output = subprocess.check_output(['uptime'], text=True)
            match = re.search(r'load average.*?([\d.]+),\s*([\d.]+),\s*([\d.]+)', output)
            if match:
                return [float(match.group(1)), float(match.group(2)), float(match.group(3))]
        except Exception as e:
            logger.error(f"获取负载失败: {e}")
        return [0.0, 0.0, 0.0]

    @staticmethod
    def _get_uptime():
        try:
            if platform.system() == "Linux":
                with open('/proc/uptime', 'r') as f:
                    uptime_seconds = float(f.readline().split()[0])
                return int(uptime_seconds)
            elif platform.system() == "Darwin":
                boot_time_str = subprocess.check_output(['sysctl', '-n', 'kern.boottime'], text=True)
                match = re.search(r'sec = (\d+)', boot_time_str)
                if match:
                    boot_time = int(match.group(1))
                    return int(time.time() - boot_time)
        except Exception as e:
            logger.error(f"获取运行时间失败: {e}")
        return 0

    @staticmethod
    def _get_process_count():
        try:
            output = subprocess.check_output(['ps', 'aux'], text=True)
            return len(output.strip().splitlines()) - 1
        except Exception as e:
            logger.error(f"获取进程数失败: {e}")
            return 0

    @staticmethod
    def _get_os_version():
        try:
            if platform.system() == "Linux":
                try:
                    with open('/etc/os-release') as f:
                        for line in f:
                            if line.startswith('PRETTY_NAME='):
                                return line.strip().split('=', 1)[1].strip('"')
                except:
                    return subprocess.check_output(['uname', '-r'], text=True).strip()
            elif platform.system() == "Darwin":
                ver = subprocess.check_output(['sw_vers', '-productVersion'], text=True).strip()
                return f"macOS {ver}"
        except Exception as e:
            logger.warning(f"获取OS版本失败: {e}")
        return "Unknown"

    @staticmethod
    def _get_network_traffic():
        try:
            if platform.system() == "Linux":
                with open('/proc/net/dev', 'r') as f:
                    lines = f.readlines()
                rx_total = tx_total = 0
                for line in lines[2:]:
                    if ':' not in line:
                        continue
                    if 'lo:' in line:
                        continue
                    iface, data = line.split(':', 1)
                    fields = data.strip().split()
                    rx_bytes = int(fields[0])
                    tx_bytes = int(fields[8])
                    rx_total += rx_bytes
                    tx_total += tx_bytes
                return {
                    "in": rx_total // (1024 ** 2),
                    "out": tx_total // (1024 ** 2)
                }
            elif platform.system() == "Darwin":
                output = subprocess.check_output(['netstat', '-ib'], text=True)
                lines = output.strip().splitlines()
                rx_total = tx_total = 0
                for line in lines[1:]:
                    fields = line.split()
                    if len(fields) >= 10 and (fields[0].startswith('en') or fields[0].startswith('eth')):
                        rx_bytes = int(fields[6])
                        tx_bytes = int(fields[9])
                        rx_total += rx_bytes
                        tx_total += tx_bytes
                return {
                    "in": rx_total // (1024 ** 2),
                    "out": tx_total // (1024 ** 2)
                }
        except Exception as e:
            logger.error(f"获取网络流量失败: {e}")
        return {"in": 0, "out": 0}
